'''
myhttp.py

Copyright 2009 Xavier Mendez Navarro aka Javi

This file is part of pysqlin

pysqlin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

pysqlin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pysqlin; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
'''

import logging
import sys
import time

try:
    import pycurl
except ImportError:
    sys.stderr.write('You need to install pycurl first\n')
    sys.exit(255)

from settings import Settings
from myexceptions import MyException
from mymodel import InjectionData
from reqresp.reqresp import Request

# Usual suspects:

#Exception in perform (35, 'error:0B07C065:x509 certificate routines:X509_STORE_add_cert:cert already in hash table')
#Exception in perform (18, 'SSL read: error:0B07C065:x509 certificate routines:X509_STORE_add_cert:cert already in hash table, errno 11')
#Exception in perform (28, 'Connection time-out')
#Exception in perform (7, "couldn't connect to host")
#Exception in perform (6, "Couldn't resolve host 'www.xxx.com'")
#(28, 'Operation timed out after 20000 milliseconds with 0 bytes received')
#Exception in perform (28, 'SSL connection timeout')

class MyRequest():
    EXCEPTION_RESOLVE_HOST = 6

    def __init__(self, req = Request()):
	self.__logger = logging.getLogger('framework.myhttp.MyRequest')

	self.req = req
	self.error = None
	self.timestamp = None
	self.fatal = None

    # ------------------------------------------------
    # Factory functions
    # ------------------------------------------------
    @staticmethod
    def from_idata(injection_data):
	# set request
	req = Request()
	proxy = Settings().get(Settings.SEC_HTTP, Settings.PROXY)
	if proxy: req.setProxy(proxy.encode('utf-8'))
	req.setUrl(injection_data[InjectionData.URL].encode('utf-8'))

	if injection_data[InjectionData.POST_DATA]:
	    req.addPostdata(injection_data[InjectionData.POST_DATA].encode('utf-8'))
	    
	if injection_data[InjectionData.COOKIE]:
	    req.addHeader("Cookie", injection_data[InjectionData.COOKIE].encode('utf-8'))

	if injection_data[InjectionData.AUTH_METHOD] and \
	    injection_data[InjectionData.AUTH_CREDENTIALS]:
		req.setAuth(injection_data[InjectionData.AUTH_METHOD], injection_data[InjectionData.AUTH_CREDENTIALS])

	# set timeouts
	conn_timeout = int(Settings().get(Settings.SEC_HTTP, Settings.CONN_TIMEOUT))
	total_timeout = int(Settings().get(Settings.SEC_HTTP, Settings.TOTAL_TIMEOUT))
	req.setConnTimeout(conn_timeout)
	req.setTotalTimeout(total_timeout)

	myreq = MyRequest(req)

	return myreq

    @staticmethod
    def from_exception(e):
	req = MyRequest(None)
	req.fatal = e

	return req

    # ------------------------------------------------
    # Print functions
    # ------------------------------------------------
    def __str__(self):
	if self.error:
	    return "%s (%s)" % (self.get_complete_url(), self.error)
	else:
	    return "%s (HTTP/%s)" % (self.get_complete_url(), str(self.get_response_code()))

    def get_raw_resp(self):
	return self.req.response.getAll()

    def get_raw_req(self):
	return self.req.getAll()

    # ------------------------------------------------
    # HTTP functions
    # ------------------------------------------------
    def get_complete_url(self):
	return self.req.completeUrl

    def has_response(self):
	return self.req.response

    def get_response_content(self):
	return self.req.response.getContent()

    def get_response_code(self):
	return self.req.response.code

    def get_response(self):
	return (self.req.response.code, self.req.response.getContent())

    def perform(self):
	return self

    # ------------------------------------------------
    # iterator functions
    # ------------------------------------------------
    def __iter__(self):
	self.__current = 0
	self.conn_retries = int(Settings().get(Settings.SEC_HTTP, Settings.CONN_RETRIES))
        return self

    def next(self):
	#if self.req.response is not None and self.req.response.code in [200,500]:
	if self.req.response is not None:
	    self.__logger.debug("next. finish, request in 200, 500.")
	    raise StopIteration
        if self.__current > self.conn_retries:
	    self.__logger.error("next. Max. retries in perform Exception, exiting.")
	    raise MyException('Max. number of HTTP request retries.')
        else:
            self.__current += 1
	    self.__logger.debug("Next. END. tries=%d, url=%s" % (self.__current, self.req.completeUrl))

	    try:
		self.req.perform()
		if self.req.response is not None:
		    if str(self.req.response.code) in Settings().get(Settings.SEC_HTTP, Settings.INVALID_CODES).split(','):
			raise MyException('Error HTTP Invalid responde code: %d' % self.req.responde.code)
		self.error = None
		self.timestamp = time.time()
		return self
	    except pycurl.error, err:
		nerror, msg = err

		if nerror == self.EXCEPTION_RESOLVE_HOST:
		    raise MyException('Couldn\'t resolve host')

		# Usgin SSL sometimes perform (pycurl) generates random exceptions => if we exit on first exception it will generate unexpected results
		# reached max retries => finish task and wait for instructions.
		self.error = msg
		self.timestamp = time.time()
		self.__logger.error("Next. Exception in perform %s" % msg)
		return self
